`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/08/26 14:14:48
// Design Name: 
// Module Name: ddr4_reader
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
/* 接口时序
rd_clk _/`\__/`\__/`\__/`\__/`\__/`\__/`\__/`\__/`\__/`\__/`\__/`\__/`\__/`\_
rd_req ______/````\_________________________________________________________
rd_busy___________/``````````````````````````````````\_________________
rd_addr xxxxx|vvvv|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
rd_len  xxxxx|vvvv|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
rd_data |xxxxxxxxxxxxxxxxxxx|vvvv|vvvv|vvvv|...v|vvvv|xxxxxxxxxxxxxxxxxxxxxx
rd_valid ___________________/```````````````...``````\______________________
rd_ready ______________/````````````````````...``````\______________________
rd_last _________________________________________/```\______________________
rd_status xxxxxxxxxxx|vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv|xxxxxxxxxxxxxxxxxxxxxx 
*/


module ddr4_reader#(
    parameter   DDR4_DATA_WIDTH         = 16                            ,
    parameter   DDR4_ADDR_WIDTH         = 32                            ,
    parameter   USER_DATA_WIDTH         = 128                           ,
    parameter   AXI_MM_BURST_LEN        = 64                            ,
    parameter   DDR4_CAPASITY           = 32'h4000_0000                  
)(
    //to user
    input   wire                        rd_clk                          ,
    input   wire                        rd_rst                          ,

    input   wire                        rd_req                          ,

    input   wire    [DDR4_ADDR_WIDTH-1:0]rd_addr                        ,
    input   wire    [15:0]              rd_len                          ,
    output  logic                       rd_busy                         ,
    output  logic   [1:0]               rd_status                       ,

    output  logic    [USER_DATA_WIDTH-1:0]rd_data                        ,
    output  logic                        rd_valid                        ,
    input   wire                        rd_ready                        ,
    output  logic                        rd_last                         ,
  

    //to ddr4 ip core
    input   wire    [DDR4_ADDR_WIDTH-1:0]RD_BASE_ADDR                   ,
    output  logic    [DDR4_ADDR_WIDTH-1:0]RD_AXI_0_araddr                ,
    output  wire    [1:0]               RD_AXI_0_arburst                ,
    output  wire    [3:0]               RD_AXI_0_arcache                ,
    output  logic    [7:0]               RD_AXI_0_arlen                  ,
    output  wire                        RD_AXI_0_arlock                 ,
    output  wire    [2:0]               RD_AXI_0_arprot                 ,
    output  wire    [3:0]               RD_AXI_0_arqos                  ,
    input   wire                        RD_AXI_0_arready                ,
    output  wire    [2:0]               RD_AXI_0_arsize                 ,
    output  logic                        RD_AXI_0_arvalid                ,
    input   wire    [USER_DATA_WIDTH-1:0]RD_AXI_0_rdata                 ,
    input   wire                        RD_AXI_0_rlast                  ,
    output  wire                        RD_AXI_0_rready                 ,
    input   wire    [1:0]               RD_AXI_0_rresp                  ,
    input   wire                        RD_AXI_0_rvalid                 

);
    //输出fifo相关信号
    logic   fifo_rst;
    logic   almost_empty_axis;
    logic   almost_full_axis;
    logic   [USER_DATA_WIDTH-1:0]fifo_wr_data;
    logic   fifo_wr_valid;
    logic   fifo_wr_ready;
    logic   fifo_wr_last;
    logic   fifo_rd_last;
    logic   [USER_DATA_WIDTH-1:0]fifo_rd_data;
    logic   fifo_rd_valid;
    logic   fifo_rd_ready;

    logic   [10:0]rd_data_count_axis;
    logic   [10:0]wr_data_count_axis;


    //缓存本次读请求的读地址
    logic   [DDR4_ADDR_WIDTH-1:0]   reg_rd_addr;
    //缓存本次读请求的读长度
    logic   [15:0]                  reg_rd_len;
    //本次读过程中已经从DDR4ip核读出来的长度，当cur_rd_len=reg_rd_len时表示已经读完请求的长度了，可以结束读操作
    logic   [15:0]                  cur_rd_cnt;
    //当前突发读的突发长度
    logic   [15:0]                  cur_burst_len;
    //本次读请求中最后一次突发读操作标志,用来产生整个用户读请求的last信号，也就是选择最后一次突发读的RD_AXI_0_rlast信号
    logic   last_burst_rd_flag;
   

    assign fifo_wr_data = RD_AXI_0_rdata;
    assign fifo_wr_valid = RD_AXI_0_rvalid;
    assign RD_AXI_0_rready = fifo_wr_ready;
    assign fifo_wr_last = (last_burst_rd_flag == 1)?RD_AXI_0_rlast : 0;

    assign rd_data = fifo_rd_data;
    assign rd_valid = fifo_rd_valid;
    assign rd_last = fifo_rd_last;
    assign fifo_rd_ready = rd_ready;

    
    assign RD_AXI_0_arburst = 2'b01;//自增突发模式
    assign RD_AXI_0_arcache = 4'b0000;
    assign RD_AXI_0_arlock = 2'b00;//访问类型 00：正常访问
    assign RD_AXI_0_arprot = 3'b000;//访问权限
    assign RD_AXI_0_arqos = 4'b0000;
    assign RD_AXI_0_arsize = $clog2(USER_DATA_WIDTH>>3);



    //状态机状态定义
    typedef enum logic[2:0] {
        IDLE,
        CHECK_ADDR,
        RD_ADDR,
        BURST_RD,
        RD_WAIT,
        RD_END
    } rd_state_t;

    rd_state_t rd_state;

    always_ff @( posedge rd_clk ) begin
        if(rd_rst == 1) begin
            rd_status <= 0;
            rd_busy <= 1;
            fifo_rst <= 1;
            last_burst_rd_flag <= 0;
   
        end
        else begin
            case (rd_state)
                IDLE : 
                begin
                    cur_rd_cnt <= 0;
                    //启动读传输
                    if(rd_req == 1 && rd_busy == 0) begin
                        rd_status <= 0;
                        rd_busy <= 1;
                        reg_rd_addr <= rd_addr;
                        reg_rd_len <= rd_len;
                        rd_state <= CHECK_ADDR;
                    end
                    else begin
                        rd_busy <= 0;
                        rd_state <= IDLE;
                    end
                end 

                CHECK_ADDR : 
                begin
                    //检查地址和长度是否有效
                    if(reg_rd_addr >= RD_BASE_ADDR && reg_rd_addr < RD_BASE_ADDR + DDR4_CAPASITY 
                        && reg_rd_len > 0 && reg_rd_len < DDR4_CAPASITY + RD_BASE_ADDR - reg_rd_addr) begin

                        //保证fifo在传输前是空的
                        if(wr_data_count_axis == 1 || rd_data_count_axis == 1) begin
                            fifo_rst <= 0;
                            rd_state <= RD_ADDR; 
                        end
                        else begin
                            fifo_rst <= 1;
                            rd_state <= CHECK_ADDR;
                        end
                    end
                    else begin
                        rd_status <= 1;
                        rd_state <= RD_END;
                    end
                end

                RD_ADDR : 
                begin
                    //如果当前的读计数器大于等于本次读请求的长度，意味着完成了读请求，跳转到RD_WAIT状态
                    if(cur_rd_cnt >= reg_rd_len) begin
                        //清除fifo，准备下一次传输
                        rd_state <= RD_WAIT;
                    end
                    else begin
                        RD_AXI_0_arvalid <= 1;
                        //更新突发读的地址
                        RD_AXI_0_araddr <= reg_rd_addr + cur_rd_cnt<<($clog2(USER_DATA_WIDTH/8));
                        //根据剩余要读的长度确定本次突发读的长度
                        if(reg_rd_len - cur_rd_cnt > AXI_MM_BURST_LEN) begin
                            
                            RD_AXI_0_arlen <= AXI_MM_BURST_LEN-1;
                            cur_burst_len <= AXI_MM_BURST_LEN;
                        end
                        else begin
                            last_burst_rd_flag <= 1;
                            RD_AXI_0_arlen <= reg_rd_len - cur_rd_cnt-1;
                            cur_burst_len <= reg_rd_len - cur_rd_cnt;
                        end
                        //写入读地址后进入突发读数据状态
                        if(RD_AXI_0_arvalid == 1 && RD_AXI_0_arready == 1) begin
                            RD_AXI_0_arvalid <= 0;
                            rd_state <= BURST_RD;
                        end
                        else begin
                            rd_state <= RD_ADDR;
                        end
                    end
                end

                BURST_RD : 
                begin
                    if(RD_AXI_0_rdata == 1 & RD_AXI_0_rready == 1) begin
                         cur_rd_cnt <= cur_rd_cnt + 1;
                        //finshed one burst write,update address
                        if(RD_AXI_0_rlast == 1) begin
                            rd_state <= RD_ADDR;
                        end
                        else begin
                            rd_state <= BURST_RD;
                        end
                    
                    end
                    else begin
                        rd_state <= BURST_RD;
                    end
                end

                RD_WAIT : 
                begin
                    //user has recive the last means the read request is done
                    if(rd_valid == 1 && rd_ready == 1 &&rd_last == 1) begin
                        rd_state <= IDLE;
                    end
                    else begin
                        rd_state <= RD_WAIT;
                    end
                end

                default: rd_state <= IDLE;
            endcase
        end
        
    end









    xpm_fifo_axis #(
        .CASCADE_HEIGHT(0),             // DECIMAL
        .CDC_SYNC_STAGES(2),            // DECIMAL
        .CLOCKING_MODE("common_clock"), // String
        .ECC_MODE("no_ecc"),            // String
        .FIFO_DEPTH(256),              // DECIMAL
        .FIFO_MEMORY_TYPE("auto"),      // String
        .PACKET_FIFO("false"),          // String
        .PROG_EMPTY_THRESH(10),         // DECIMAL
        .PROG_FULL_THRESH(1000),          // DECIMAL
        .RD_DATA_COUNT_WIDTH(10),        // DECIMAL
        .RELATED_CLOCKS(0),             // DECIMAL
        .SIM_ASSERT_CHK(0),             // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
        .TDATA_WIDTH(USER_DATA_WIDTH),               // DECIMAL
        .TDEST_WIDTH(1),                // DECIMAL
        .TID_WIDTH(1),                  // DECIMAL
        .TUSER_WIDTH(1),                // DECIMAL
        .USE_ADV_FEATURES("1000"),      // String
        .WR_DATA_COUNT_WIDTH(10)         // DECIMAL
    )
    xpm_fifo_axis_inst (
        .almost_empty_axis(almost_empty_axis),   // 1-bit output: Almost Empty : When asserted, this signal
                                                // indicates that only one more read can be performed before the
                                                // FIFO goes to empty.

        .almost_full_axis(almost_full_axis),     // 1-bit output: Almost Full: When asserted, this signal
                                                // indicates that only one more write can be performed before
                                                // the FIFO is full.

        .dbiterr_axis(),             // 1-bit output: Double Bit Error- Indicates that the ECC
                                                // decoder detected a double-bit error and data in the FIFO core
                                                // is corrupted.

        .m_axis_tdata(fifo_rd_data),             // TDATA_WIDTH-bit output: TDATA: The primary payload that is
                                                // used to provide the data that is passing across the
                                                // interface. The width of the data payload is an integer number
                                                // of bytes.

        .m_axis_tdest(),             // TDEST_WIDTH-bit output: TDEST: Provides routing information
                                                // for the data stream.

        .m_axis_tid(),                 // TID_WIDTH-bit output: TID: The data stream identifier that
                                                // indicates different streams of data.

        .m_axis_tkeep(),             // TDATA_WIDTH/8-bit output: TKEEP: The byte qualifier that
                                                // indicates whether the content of the associated byte of TDATA
                                                // is processed as part of the data stream. Associated bytes
                                                // that have the TKEEP byte qualifier deasserted are null bytes
                                                // and can be removed from the data stream. For a 64-bit DATA,
                                                // bit 0 corresponds to the least significant byte on DATA, and
                                                // bit 7 corresponds to the most significant byte. For example:
                                                // KEEP[0] = 1b, DATA[7:0] is not a NULL byte KEEP[7] = 0b,
                                                // DATA[63:56] is a NULL byte

        .m_axis_tlast(fifo_rd_last),             // 1-bit output: TLAST: Indicates the boundary of a packet.
        .m_axis_tstrb(),             // TDATA_WIDTH/8-bit output: TSTRB: The byte qualifier that
                                                // indicates whether the content of the associated byte of TDATA
                                                // is processed as a data byte or a position byte. For a 64-bit
                                                // DATA, bit 0 corresponds to the least significant byte on
                                                // DATA, and bit 0 corresponds to the least significant byte on
                                                // DATA, and bit 7 corresponds to the most significant byte. For
                                                // example: STROBE[0] = 1b, DATA[7:0] is valid STROBE[7] = 0b,
                                                // DATA[63:56] is not valid

        .m_axis_tuser(),             // TUSER_WIDTH-bit output: TUSER: The user-defined sideband
                                                // information that can be transmitted alongside the data
                                                // stream.

        .m_axis_tvalid(fifo_rd_valid),           // 1-bit output: TVALID: Indicates that the master is driving a
                                                // valid transfer. A transfer takes place when both TVALID and
                                                // TREADY are asserted

        .prog_empty_axis(),       // 1-bit output: Programmable Empty- This signal is asserted
                                                // when the number of words in the FIFO is less than or equal to
                                                // the programmable empty threshold value. It is de-asserted
                                                // when the number of words in the FIFO exceeds the programmable
                                                // empty threshold value.

        .prog_full_axis(),         // 1-bit output: Programmable Full: This signal is asserted when
                                                // the number of words in the FIFO is greater than or equal to
                                                // the programmable full threshold value. It is de-asserted when
                                                // the number of words in the FIFO is less than the programmable
                                                // full threshold value.

        .rd_data_count_axis(rd_data_count_axis), // RD_DATA_COUNT_WIDTH-bit output: Read Data Count- This bus
                                                // indicates the number of words available for reading in the
                                                // FIFO.

        .s_axis_tready(fifo_wr_ready),           // 1-bit output: TREADY: Indicates that the slave can accept a
                                                // transfer in the current cycle.

        .sbiterr_axis(),             // 1-bit output: Single Bit Error- Indicates that the ECC
                                                // decoder detected and fixed a single-bit error.

        .wr_data_count_axis(wr_data_count_axis), // WR_DATA_COUNT_WIDTH-bit output: Write Data Count: This bus
                                                // indicates the number of words written into the FIFO.

        .injectdbiterr_axis(), // 1-bit input: Double Bit Error Injection- Injects a double bit
                                                // error if the ECC feature is used.

        .injectsbiterr_axis(), // 1-bit input: Single Bit Error Injection- Injects a single bit
                                                // error if the ECC feature is used.

        .m_aclk(rd_clk),                         // 1-bit input: Master Interface Clock: All signals on master
                                                // interface are sampled on the rising edge of this clock.

        .m_axis_tready(fifo_rd_ready),           // 1-bit input: TREADY: Indicates that the slave can accept a
                                                // transfer in the current cycle.

        .s_aclk(rd_clk),                         // 1-bit input: Slave Interface Clock: All signals on slave
                                                // interface are sampled on the rising edge of this clock.

        .s_aresetn(~fifo_rst),                   // 1-bit input: Active low asynchronous reset.
        .s_axis_tdata(fifo_wr_data),             // TDATA_WIDTH-bit input: TDATA: The primary payload that is
                                                // used to provide the data that is passing across the
                                                // interface. The width of the data payload is an integer number
                                                // of bytes.

        .s_axis_tdest(0),             // TDEST_WIDTH-bit input: TDEST: Provides routing information
                                                // for the data stream.

        .s_axis_tid(0),                 // TID_WIDTH-bit input: TID: The data stream identifier that
                                                // indicates different streams of data.

        .s_axis_tkeep(16'hffff),             // TDATA_WIDTH/8-bit input: TKEEP: The byte qualifier that
                                                // indicates whether the content of the associated byte of TDATA
                                                // is processed as part of the data stream. Associated bytes
                                                // that have the TKEEP byte qualifier deasserted are null bytes
                                                // and can be removed from the data stream. For a 64-bit DATA,
                                                // bit 0 corresponds to the least significant byte on DATA, and
                                                // bit 7 corresponds to the most significant byte. For example:
                                                // KEEP[0] = 1b, DATA[7:0] is not a NULL byte KEEP[7] = 0b,
                                                // DATA[63:56] is a NULL byte

        .s_axis_tlast(fifo_wr_last),             // 1-bit input: TLAST: Indicates the boundary of a packet.
        .s_axis_tstrb(16'h0000),             // TDATA_WIDTH/8-bit input: TSTRB: The byte qualifier that
                                                // indicates whether the content of the associated byte of TDATA
                                                // is processed as a data byte or a position byte. For a 64-bit
                                                // DATA, bit 0 corresponds to the least significant byte on
                                                // DATA, and bit 0 corresponds to the least significant byte on
                                                // DATA, and bit 7 corresponds to the most significant byte. For
                                                // example: STROBE[0] = 1b, DATA[7:0] is valid STROBE[7] = 0b,
                                                // DATA[63:56] is not valid

        .s_axis_tuser(0),             // TUSER_WIDTH-bit input: TUSER: The user-defined sideband
                                                // information that can be transmitted alongside the data
                                                // stream.

        .s_axis_tvalid(fifo_wr_valid)            // 1-bit input: TVALID: Indicates that the master is driving a
                                                // valid transfer. A transfer takes place when both TVALID and
                                                // TREADY are asserted

    );

endmodule
